package com.ygl.example.service.rest;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.ygl.example.service.exception.ExceptionUtil;
import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

@RestController
@ConditionalOnProperty(name = "spring.application.name", havingValue = "example-service-a")
public class ARestImpl {
    private static final Logger LOG = LoggerFactory.getLogger(ARestImpl.class);

    @Autowired
    private RestTemplate restTemplate;

    @Value("${server.port}")
    private String port;

    @GetMapping("/rest")
    public String rest(){
        String url = "http://example-service-b/rest";
        ResponseEntity<String> entity = restTemplate.getForEntity(url, String.class, "");
        String body = entity.getBody();
        return body;
    }

    @GetMapping("/restB")
    public String restB(){
        String url = "http://example-service-b/test";
        ResponseEntity<String> entity = restTemplate.getForEntity(url, String.class, "");
        String body = entity.getBody();
        return body;
    }

    // 对应的 `handleException` 函数需要位于 `ExceptionUtil` 类中，并且必须为 static 函数.
    @SentinelResource(value = "test", blockHandler = "handleException", blockHandlerClass = {ExceptionUtil.class},fallbackClass = {ExceptionUtil.class}, fallback = "fallback")
    @GetMapping("/test")
    public String test() throws InterruptedException {
//        int b = 1/0;
        Thread.sleep(200);
//        return "test - a-:" + port;
        throw new RuntimeException("xx");
    }

    /**
     * blockHandler 对应处理 BlockException 的函数名称，
     * 若 blockHandler 和 fallback 都进行了配置，则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。
     * 若未配置 blockHandler、fallback 和 defaultFallback，则被限流降级时会将 BlockException
     * 直接抛出（若方法本身未定义 throws BlockException 则会被 JVM 包装一层 UndeclaredThrowableException）。
     * fallback 用于在抛出异常的时候提供 fallback 处理逻辑
     */
    // 原函数
    @SentinelResource(value = "hello", blockHandler = "exceptionHandler", fallback = "helloFallback")
    @GetMapping("/hello/{s}")
    public String hello(@PathVariable long s) {
        int b = 1/0;
        return String.format("Hello at %d", s);
    }

    // Fallback 函数，函数签名与原函数一致或加一个 Throwable 类型的参数.
    public String helloFallback(long s) {
        return String.format("Halooooo %d", s);
    }

    // Block 异常处理函数，参数最后多一个 BlockException，其余与原函数一致.
    public String exceptionHandler(long s, BlockException ex) {
        // Do some log here.
        ex.printStackTrace();
        return "Oops, error occurred at " + s;
    }

    @SentinelResource("test2")
    @GetMapping("/test2")
    public String test2(@RequestParam(required = false) String a,
                        @RequestParam(required = false) String b) throws InterruptedException {
//        int b = 1/0;
        return  a+"--"+b;
//        Thread.sleep(200);
//        return "test - a-:" + port;
//        throw new RuntimeException("xx");
    }

    /**
     * blockHandler: 进入程序之前，限流等时发生
     * handleFallback: 进入程序之后，程序异常时发生
     * @param a
     * @param b
     * @return
     */
    @SneakyThrows
    @SentinelResource(value = "hehe", blockHandler = "handleBlockException", fallback = "handleFallback")
    @GetMapping("/hehe")
    public String hehe(@RequestParam(required = false) String a,
                        @RequestParam(required = false) String b) {
        Thread.sleep(200);
        int i = 1/0;
        return  a+"--"+b;
    }
    // Block 异常处理函数，参数最后多一个 BlockException，其余与原函数一致.
    public String handleBlockException(String a,String b, BlockException ex) {
        ex.printStackTrace();
        if (ex instanceof AuthorityException) {
            return String.format("Oops, AuthorityException at {%s},{%s}", a, b);
        }
        return String.format("Oops, Block occurred at {%s},{%s}", a, b);
    }
    // Fallback 函数，函数签名与原函数一致或加一个 Throwable 类型的参数.
    public String handleFallback(String a,String b, Throwable ex) {
        ex.printStackTrace();
        return String.format("Oops, error occurred at {%s},{%s}", a, b);
    }

}